@use '../core/tokens/token-utils';
@use '../core/style/layout-common';

// Adds styles necessary to provide stateful interactions with the button. This includes providing
// content for the state container's ::before and ::after so that they can be given a background
// color and opacity for states like hover, active, and focus. Additionally, adds styles to the
// ripple and state container so that they fill the button, match the border radius, and avoid
// pointer events.
@mixin mat-private-button-interactive($focus-indicator-inherits-shape: true) {
  -webkit-tap-highlight-color: transparent;

  // The ripple container should match the bounds of the entire button.
  .mat-mdc-button-ripple,
  .mat-mdc-button-persistent-ripple,
  .mat-mdc-button-persistent-ripple::before {
    @include layout-common.fill;

    // Disable pointer events for the ripple container and state overlay because the container
    // will overlay the user content and we don't want to disable mouse events on the user content.
    // Pointer events can be safely disabled because the ripple trigger element is the host element.
    pointer-events: none;

    // Inherit the border radius from the parent so that state overlay and ripples don't exceed the
    // parent button boundaries. Note that an inherited border radius does not work properly if
    // the actual button element does have a border because it causes the inner content to be
    // smaller. We have special logic for stroked buttons to handle this scenario.
    border-radius: inherit;
  }

  // This style used to be applied by the MatRipple
  // directive, which is no longer attached to this element.
  .mat-mdc-button-ripple {
    overflow: hidden;
  }

  // We use ::before so that we can reuse some of MDC's theming.
  .mat-mdc-button-persistent-ripple::before {
    content: '';
    opacity: 0;
  }

  // The content should appear over the state and ripple layers, otherwise they may adversely affect
  // the accessibility of the text content.
  .mdc-button__label,
  .mat-icon {
    z-index: 1;
    position: relative;
  }

  // The focus indicator should match the bounds of the entire button.
  .mat-focus-indicator {
    @include layout-common.fill();

    @if ($focus-indicator-inherits-shape) {
      border-radius: inherit;
    }
  }

  &:focus > .mat-focus-indicator::before {
    content: '';

    @if ($focus-indicator-inherits-shape) {
      border-radius: inherit;
    }
  }
}

@mixin mat-private-button-ripple(
    $ripple-color-token, $state-layer-color-token, $disabled-state-layer-color-token,
    $hover-state-layer-opacity-token, $focus-state-layer-opacity-token,
    $pressed-state-layer-opacity-token, $fallbacks
) {
  .mat-ripple-element {
    background-color: token-utils.slot($ripple-color-token, $fallbacks);
  }

  .mat-mdc-button-persistent-ripple::before {
    background-color: token-utils.slot($state-layer-color-token, $fallbacks);
  }

  &.mat-mdc-button-disabled .mat-mdc-button-persistent-ripple::before {
    background-color: token-utils.slot($disabled-state-layer-color-token, $fallbacks);
  }

  &:hover > .mat-mdc-button-persistent-ripple::before {
    opacity: token-utils.slot($hover-state-layer-opacity-token, $fallbacks);
  }

  &.cdk-program-focused,
  &.cdk-keyboard-focused,
  &.mat-mdc-button-disabled-interactive:focus {
    > .mat-mdc-button-persistent-ripple::before {
      opacity: token-utils.slot($focus-state-layer-opacity-token, $fallbacks);
    }
  }

  &:active > .mat-mdc-button-persistent-ripple::before {
    opacity: token-utils.slot($pressed-state-layer-opacity-token, $fallbacks);
  }
}

// MDC's disabled buttons define a default cursor with pointer-events none. However, they select
// :disabled for this, which does not affect anchor tags.
// TODO(andrewseguin): Discuss with the MDC team about a mixin we can call for applying this style,
// and note that having pointer-events may have unintended side-effects, e.g. allowing the user
// to click the target underneath the button.
@mixin mat-private-button-disabled() {
  // `[disabled]` shouldn't be necessary, but we keep it to maintain
  // compatibility with apps setting it through host bindings.
  &[disabled],
  &.mat-mdc-button-disabled {
    cursor: default;
    pointer-events: none;
    @content;
  }

  &.mat-mdc-button-disabled-interactive {
    pointer-events: auto;
  }
}

@mixin mat-private-button-touch-target($is-square, $touch-target-display-token, $fallbacks) {
  .mat-mdc-button-touch-target {
    position: absolute;
    top: 50%;
    height: 48px;
    display: token-utils.slot($touch-target-display-token, $fallbacks);

    @if $is-square {
      left: 50%;
      width: 48px;
      transform: translate(-50%, -50%);
    } @else {
      left: 0;
      right: 0;
      transform: translateY(-50%);
    }
  }
}

@mixin mat-private-button-horizontal-layout(
    $icon-spacing-token, $icon-offset-token, $with-icon-horizontal-padding-token, $fallbacks) {
  $icon-spacing: token-utils.slot($icon-spacing-token, $fallbacks, true);
  $icon-offset: token-utils.slot($icon-offset-token, $fallbacks, true);

  @if ($with-icon-horizontal-padding-token) {
    // stylelint-disable-next-line selector-class-pattern
    &:has(.material-icons, mat-icon, [matButtonIcon]) {
      padding: 0 token-utils.slot($with-icon-horizontal-padding-token, $fallbacks, true);
    }
  }

  // MDC expects button icons to contain this HTML content:
  // ```html
  //   <span class="mdc-button__icon material-icons">favorite</span>
  // ```
  // However, Angular Material expects a `mat-icon` instead. The following
  // styles will lay out the icons appropriately.
  & > .mat-icon {
    margin-right: $icon-spacing;
    margin-left: $icon-offset;

    [dir='rtl'] & {
      margin-right: $icon-offset;
      margin-left: $icon-spacing;
    }
  }

  .mdc-button__label + .mat-icon {
    margin-right: $icon-offset;
    margin-left: $icon-spacing;

    [dir='rtl'] & {
      margin-right: $icon-spacing;
      margin-left: $icon-offset;
    }
  }
}
